#include "FABase/thread.h"

CYKThread::CYKThread(const RunnableAutoPt &pt, bool detached/* =false */)
: _use_functor(true),
_functor(pt),
_func_ptr(NULL),
_func_arg(NULL),
_detached(detached),
_state(INIT),
_isstopped(false)
#ifdef WIN32
, _handle(NULL)
#endif
{ }

CYKThread::CYKThread(run_func_t func, void *arg /* = NULL */, bool detached /* = false */)
: _use_functor(false),
_functor(0),
_func_ptr(func),
_func_arg(arg),
_detached(detached),
_state(INIT),
_isstopped(false)
#ifdef WIN32
, _handle(NULL)
#endif
{ }

CYKThread::~CYKThread() {
	join();
	_state = STOP;
}

bool CYKThread::start() {
	if (INIT != _state) {
		return false;
	}
#ifdef WIN32
	_handle = ::CreateThread(NULL, 0,
		(LPTHREAD_START_ROUTINE)thread_start_func, this, 0, &_thread_id);

	if (NULL != _handle) {
		_sema.wait(1);
		if (_detached)
		{
			CloseHandle(_handle);
		}
	}
	_isstopped = false;
	_state = START;

	return (NULL != _handle);
#else
	int ret;
	pthread_attr_t attr;

	ret = pthread_attr_init(&attr);
	if (0 != ret) {
		return false;
	}

	ret = pthread_attr_setdetachstate(&attr,
		_detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE);
	if (0 != ret) {
		pthread_attr_destroy(&attr);
		return false;
	}

	ret = pthread_create(&_thread, &attr, thread_start_func, (void*)this);

	if (0 == ret) {
		_sema.wait();
	}

	pthread_attr_destroy(&attr);

	if (0 != ret) {
		return false;
	}

	_isstopped = false;
	_state = START;
	return true;
#endif
}

bool CYKThread::join() {
	if (START != _state && STOP != _state) {
		return false;
	}
#ifdef WIN32
	bool ret = false;
	if (!_detached) {
		if (NULL != _handle) {
			DWORD dw = ::WaitForSingleObject(_handle, INFINITE);
			if (WAIT_OBJECT_0 == dw || WAIT_ABANDONED == dw) {
				CloseHandle(_handle);
				ret = true;
				_handle = NULL;
			}
		}
		_state = JOINED;
	}
	return ret;
#else
	bool ret = false;

	if (!pthread_equal(pthread_self(), _thread)) {
		if (!_detached) {
			int r = pthread_join(_thread, NULL);
			if (0 == r) {
				_state = JOINED;
			}
			ret = (0 == r);
		}
	}
	else {
		ret = false;
	}
	return ret;
#endif 
}

bool CYKThread::stop() {
	bool ret = false;
	if (_state != STOP && _state >= START)
	{
		_isstopped = true;
		_state = STOP;
		ret = true;
	}
	return ret;
}

#ifdef WIN32
DWORD CYKThread::get_thread_id() const {
	return _thread_id;
}

CYKThread::operator HANDLE() {
	return _handle;
}

DWORD __stdcall CYKThread::thread_start_func(LPVOID lpParam) {
	CYKThread *me = (CYKThread*)lpParam;
	RunnableAutoPt runner = me->_functor;
	bool use_functor = me->_use_functor;
	run_func_t func = me->_func_ptr;
	void* arg = me->_func_arg;
	me->_sema.signal();

	DWORD ret = 0;
	if (use_functor) {
		if (runner.isValid()) {
			ret = (*runner)(&me->_isstopped, arg);
		}
	}
	else {
		if (func) {
			func(&me->_isstopped, arg);
		}
	}

	return ret;
}
#else
pthread_t CYKThread::get_thread_id() const {
	return _thread;
}

void* CYKThread::thread_start_func(void *arg) {
	CYKThread* me = (CYKThread*)arg;
	RunnableAutoPt runner = me->_functor;
	bool use_functor = me->_use_functor;
	run_func_t func = me->_func_ptr;
	void* func_arg = me->_func_arg;
	me->_sema.signal();

	if (use_functor) {
		if (runner.isValid()) {
			(*runner)(&me->_isstopped);
		}
	}
	else {
		if (func) {
			func(&me->_isstopped, func_arg);
		}
	}
	return NULL;
}
#endif